home *** CD-ROM | disk | FTP | other *** search
- /* EraseScribble
- * Copyright (c) 1994 J Robert Boonstra
- *
- * Problem statement:
- *
- * Determine whether a square eraser of diameter eraserSize
- * centered at the thePoint intersects any of the points
- * in the data structure theScribble. Note that while the
- * problem statement refers to line segments, the definition
- * of a "hit" means that only the endpoints matter.
- *
- * Solution strategy:
- *
- * The ideal approach would be to create a simple bitMap
- * during Initialization indicating whether an eraser at a
- * given location intersected theScribble.
- * The bitMap would be created by stamping a cursor image
- * at the location of each point in theScribble.
- * However, a bitMap covering the required maximum bounding
- * box of 1024 x 1024 would require 2^17 bytes, or four
- * times as much storage as the 32K we are allowed to use.
- *
- * Therefore, this solution has three cases:
- * 1) If the actual bounding box for theScribble passed to
- * the init function fits in the 32K available, we
- * create a bitMap as above and use it directly.
- * 2) Otherwise, we attempt to create a half-scale bitMap,
- * where each bit represents 4 pixels in the image, 2 in
- * .h and 2 in .v. In the PtInScribble function, we can
- * then quickly determine in most cases when the eraser
- * does not intersect theScribble, and we have to walk
- * the points in theScribble if the bitMap indicates a
- * possible hit.
- * 3) In the event there is not enough storage for a
- * half-scale bitmap, we create a quarter-scale bitmap,
- * where each bit represents 16 pixels in the image, 4
- * in .h and 4 in .v. Then we proceed as in case 2.
- *
- * To optimize examination of the points in theScribble
- * when the bitmap is not full scale, we sort the points
- * in the initialization function and store them in the
- * privateScribbleDataPtr. Although this reduces the amount
- * of storage available for the bitmap by ~2K, it improves
- * worst case performance significantly.
- *
- * Although the init function is not timed for score, we
- * have written it in assembler, in the spirit of the
- * September Challenge.
- */
-
- #pragma options(assign_registers,honor_register,mc68020)
-
- /*
- * Typedefs, defines, and prototoypes
- */
-
- #define ushort unsigned short
- #define ulong unsigned long
- #define kTotalStorage 0x8000
- /*
- * Layout of privateScribbleData storage:
- * OFFSET CONTENT
- * ------ -------
- * 0: bitMap
- * kTotalStorage-kGlobals-4*gNumPoints: sorted points
- * kTotalStorage-kGlobals: global data
- *
- * Globals stored in PrivateScribbleDataPtr
- * gNumPoints:number of points in the scribble
- * gHOrigin: min scribble h - eraserSize/2
- * gVOrigin: min scribble v - eraserSize/2
- * gBMHeight: max scribble h - min scribble h + eraserSize
- * gBMWidth: max scribble v - min scribble v + eraserSize
- * gRowBytes: bitMap rowBytes
- * gMode: flag indicating bitmap scale
- */
- #define gNumPoints kTotalStorage-2
- #define gHOrigin kTotalStorage-4
- #define gVOrigin kTotalStorage-6
- #define gBMHeight kTotalStorage-8
- #define gBMWidth kTotalStorage-10
- #define gRowBytes kTotalStorage-12
- #define gMode kTotalStorage-14
- #define kGlobals 14
- #define kSortedPoints kTotalStorage-kGlobals
- #define kBitMap 0
- #define kHalfscaleBitMap 1
- #define kQrtrscaleBitMap -1
-
- typedef struct Scribble {
- Point startingPoint;
- Point deltaPoints[1];
- } Scribble, *ScribblePtr, **ScribbleHndl;
-
- void *EraseScribbleInit(ScribbleHndl theScribble,
- unsigned short eraserSize);
-
- Boolean PtInScribble(Point thePoint,
- ScribbleHndl theScribble,
- unsigned short eraserSize,
- void *privateScribbleDataPtr);
-
-
- /*
- * ------------------------------------------- PtInScribble
- */
- #define eraserH D0
- #define eraserV D1
- #define eraserSz D2
- #define pointCt D6
- #define scribblePt D7
-
- Boolean PtInScribble(Point thePoint,
- ScribbleHndl theScribble,
- unsigned short eraserSize,
- void *privateScribbleDataPtr)
- {
- asm 68020 {
- MOVEM.L D6-D7,-(A7)
- MOVE.L thePoint,D1
- MOVEA.L privateScribbleDataPtr,A0
- MOVEQ #0,D0 ; clear high bits for BFTST
- MOVE.W D1,D0
- SWAP D1
- ; Return noHit if eraser is outside bounding box defined
- ; by gVOrigin,gHOrigin,gVOrigin+gBMHeight,gHOrigin+gBMWIdth.
- ; Note that the bounding box has already been expanded by
- ; eraserSize/2 in each direction.
- SUB.W gVOrigin(A0),D1
- BLT @noHit ; v < boundingBox.top
- CMP.W gBMHeight(A0),D1
- BGT @noHit ; v > boundingBox.bottom
- SUB.W gHOrigin(A0),D0
- BLT @noHit ; h < boundingBox.left
- CMP.W gBMWidth(A0),D0
- BGT @noHit ; h > boundingBox.right
- ;
- ; Check eraser against bitMap; return noHit if not set
- ;
- MOVE.W gRowBytes(A0),D2
- TST.W gMode(A0)
- BNE.S @testQrtrScaleBitMap
- ;
- ; Full-scale bitMap case; can return Hit if bit is set
- ;
- MULU.W D1,D2 ; multiple row by rowBytes
- ADDA D2,A0 ; A0 points to correct bitMap row
- BFTST (A0){D0:1} ; D0 contains bit offset
- BNE @hit
- noHit:
- MOVEQ #0,D0
- MOVEM.L (A7)+,D6-D7
- UNLK A6 ; save one branch by returning directly
- RTS
- testQrtrScaleBitMap:
- BGT @testHalfScaleBitMap
- ;
- ; Qrtr-scale bitMap case; cannot always return Hit if set
- ;
- LSR.W #2,D1 ; * adjust for qrtr-scale bitmap *
- LSR.W #2,D0 ; * adjust for qrtr-scale bitmap *
- MULU.W D1,D2 ; multiple row by rowBytes
- ADDA D2,A0 ; A0 points to correct bitMap row
- BFTST (A0){D0:1}
- BEQ @noHit
- BRA.S @TestPoints
- testHalfScaleBitMap:
- ;
- ; Half-scale bitMap case; cannot always return Hit if set
- ;
- LSR.W #1,D1 ; * adjust for half-scale bitmap *
- LSR.W #1,D0 ; * adjust for half-scale bitmap *
- MULU.W D1,D2 ; multiple row by rowBytes
- ADDA.L D2,A0 ; A0 points to correct bitMap row
- BFTST (A0){D0:1}
- BEQ @noHit
- TestPoints:
- ;
- ; bitMap indicates there might be a hit, need to check
- ;
- MOVEA.L privateScribbleDataPtr,A0
- MOVE.W eraserSize,eraserSz
- MOVE.L thePoint,eraserH
- MOVE.L eraserH,eraserV
- SWAP eraserV
- LEA kSortedPoints(A0),A1
- ; Scan sets of 64 points to find a close match
- MOVE.W gNumPoints(A0),D7
- LSR.W #6,D7 ; numPoints/64
- MOVE.W D7,pointCt
- LSL.W #8,D7 ; times 4 bytes/pt * 64 pts
- SUBA.L d7,A1
- SUBQ #1,pointCt
- eightLoop:
- MOVE.L -(A1),scribblePt
- CMP.W eraserH,scribblePt
- BLT.S @hLoop
- ADDI #65*4,A1
- DBRA pointCt,@eightLoop;
- ; Note that the sorted scribblePts have been stored with
- ; eraserSize/2 already added in h and v
- hLoop:
- MOVE.L -(A1),scribblePt
- CMP.W eraserH,scribblePt
- BLT.S @hLoop
- ; All points from here on have a true .h component >=
- ; eraserH-eraserSize/2. Now need to look at those where
- ; .h <= eraserH+eraserSize/2. Because we already added
- ; eraserSize/2 to the sorted point values, we compare
- ; against eraserH+eraserSz
- ADD.W eraserSz,eraserH
- ADD.W eraserV,eraserSz
- vLoop:
- CMP.W eraserH,scribblePt
- BGT.S @noHit
- ; scribblePt.h is in range, now check .v
- SWAP scribblePt
- CMP.W eraserV,scribblePt
- BLT.S @vLoopCheck
- SUB.W eraserSz,scribblePt
- BLE.S @hit
- vLoopCheck:
- MOVE.L -(A1),scribblePt
- BRA @vLoop;
-
- hit:
- MOVEQ #1,D0
- MOVEM.L (A7)+,D6-D7
- }
- }
-
- /*
- * -------------------------------------- EraseScribbleInit
- */
- void *EraseScribbleInit(ScribbleHndl theScribble,
- unsigned short eraserSize)
- {
- ulong bitMapStorageAvail;
- asm 68020 {
- MOVEM.L D3-D7/A2,-(A7)
- ; Allocate storage - use full 32K.
- ; Note that it is the responsibility of the caller to
- ; release this storage.
- MOVE.L #kTotalStorage,D0
- NewPtr CLEAR
- MOVE.L A0,A2 ; A0 is privateDataPtr
- ; Scan thru the scribble to find min and max in h and v
- MOVEA.L theScribble,A1
- MOVEA.L (A1),A2 ; A2 = *theScribble
- ; Initialize mins and maxes to be the starting point
- MOVE.L (A2)+,D7 ; current scribble point
- MOVEQ #0,D5 ; clear high bits for ADDA.L later
- MOVE.W D7,D5 ; D5 = max h
- MOVE.W D7,D4 ; D4 = min h
- MOVE.W D7,D1 ; D1 = current h
- SWAP D7 ; D7 = max v
- MOVE.W D7,D6 ; D6 = min
- MOVE.W D7,D2 ; D2 = current v
- LEA kSortedPoints(A0),A1; sorted point storage
- ; Set up eraserSize/2
- MOVE.W eraserSize,D3
- LSR.W #1,D3 ; D3 = eraserSize/2
- minMaxLoop:
- ; Store point for subsequent sorting
- MOVE.W D1,D0
- ADD.W D3,D0
- MOVE.W D0,-(A1) ; store current h + eraserSize/2
- MOVE.W D2,D0
- ADD.W D3,D0
- MOVE.W D0,-(A1) ; store current v + eraserSize/2
- ; Fetch next point
- MOVE.L (A2)+,D0 ; fetch deltaPoint
- BEQ @minMaxDone
- ADD.W D0,D1 ; update current h
- CMP.W D1,D5
- BGE @noNewHMax
- MOVE.W D1,D5 ; store new max h
- BRA.S @noNewHMin
- noNewHMax:
- CMP.W D1,D4
- BLE @noNewHMin
- MOVE.W D1,D4 ; store new min h
- noNewHMin:
- SWAP D0
- ADD.W D0,D2 ; update current v
- CMP.W D2,D7
- BGE @noNewVMax
- MOVE.W D2,D7 ; store new max v
- BRA.S @noNewVMin
- noNewVMax:
- CMP.W D2,D6
- BLE @noNewVMin
- MOVE.W D2,D6 ; store new min v
- noNewVMin:
- BRA.S @minMaxLoop
- minMaxDone:
-
- ; Calculate number of points
- LEA kSortedPoints(A0),A2
- MOVE.L A2,D0
- SUB.L A1,D0
- LSR.W #2,D0
- MOVE.W D0,gNumPoints(A0)
- ; Calculate bitmap storage available
- SUBQ.L #8,A1 ; Reserve room for sentinals
- MOVE.L A1,D0
- SUB.L A0,D0
- MOVE.L D0,bitMapStorageAvail
- ; Calculate origin = min h/v minus eraserSize/2
- MOVE.W eraserSize,D0
- ; Calculate number of columns
- SUB.W D3,D4 ; adjust h origin for eraserSize/2
- MOVE.W D4,gHOrigin(A0) ; D4 = H Origin
- ADD.W D0,D5 ; add eraserSize to width
- SUB.W D4,D5
- MOVE.W D5,gBMWidth(A0)
- ADDQ #7,D5 ; round up to byte level
- LSR.W #3,D5
- MOVE.W D5,gRowBytes(A0) ; D5 = rowBytes
- ; Calculate number of rows
- SUB.W D3,D6 ; adjust v origin for eraserSize/2
- MOVE.W D6,gVOrigin(A0) ; D6 = V Origin
- ADD.W D0,D7 ; add eraserSize to height
-
- SUB.W D6,D7
- MOVE.W D7,gBMHeight(A0) ; D7 = number of rows
- ADDQ #1,D7
- ; Calculate number of bytes needed to store bitmap.
- MULU.W D5,D7
- CMP.L bitMapStorageAvail,D7
- BLE.S @haveEnoughStorage
- ;
- ; Not enough storage, so we try a half-scale bitMap
- ;
- MOVEQ #kHalfscaleBitMap,D1
- MOVE.W D1,gMode(A0)
- ; Adjust gRowBytes for half-scale bitMap
- MOVE.W gBMWidth(A0),D5
- ADDI #15,D5 ; round up to byte level
- LSR.W #4,D5
- MOVE.W D5,gRowBytes(A0) ; D5 = rowBytes
- LSR.W #1,D0 ; adjust eraser size
- ; for half-scale bitmap
- MOVE.W gBMHeight(A0),D7
- ADDQ #1,D7
- LSR.W #1,D7
- ADDQ #1,D7
- MULU.W D5,D7
- CMP.L bitMapStorageAvail,D7
- BLE.S @haveEnoughStorage
- ;
- ; Still not enough storage, so we set up a qrtr-scale bitMap
- ;
- MOVEQ #kQrtrscaleBitMap,D1
- MOVE.W D1,gMode(A0)
- ; Adjust gRowBytes for qrtr-scale bitMap
- MOVE.W gBMWidth(A0),D5
- ADDI #31,D5 ; round up to byte level
- LSR.W #5,D5
- MOVE.W D5,gRowBytes(A0) ; D5 = rowBytes
- LSR.W #1,D0 ; adjust eraser size
- ; for qrtr-scale bitmap
-
- haveEnoughStorage:
- ;
- ; Create bitMap
- ;
- MOVEA.L theScribble,A1
- MOVEA.L (A1),A2
- ;
- ; Initial location to stamp eraser image in bitmap is the
- ; startingPoint, minus the bitmap origin, minus eraserSize/2
- ;
- MOVE.L (A2)+,D2 ; Fetch startingPoint
- MOVE.W D2,D1 ; D1 = current scribble point h
- SWAP D2 ; D2 = current scribble point v
- SUB.W D4,D1 ; D1 = scribble point h - H origin
- SUB.W D3,D1 ; offset h by -eraserSize/2
- SUB.W D6,D2 ; D2 = scribble point v - V origin
- SUB.W D3,D2 ; offset v by -eraserSize/2
- MOVEQ #0,D4 ; clear high bits for BFINS later
-
- MOVE.W D0,D7 ; save eraser width
-
- MOVEQ #-1,D3 ; set bits for insertion using BFINS;
- ; Loop through all points in the scribble
- ;
- stampPointLoop:
- MOVEA.L A0,A1
- MOVE D7,D6 ; D6 = row counter for DBRA smear
-
- MOVE.W D2,D0 ; D0 = v - origin
-
- TST.W gMode(A0)
- BEQ @L1
- BGT @L0
- LSR.W #1,D0 ; adjust for qrtr-scale bitmap
- L0: LSR.W #1,D0 ; adjust for half-scale bitmap
- L1:
- MULU.W D5,D0 ; D0 = (v - origin) * rowBytes
- ADDA.L D0,A1 ; A1 = privateStorage - rowOffset
- MOVE.W D7,D0
- ADDQ #1,D0 ; D0 = eraserSize/2+1, the number of
- ; bits to set in bitMap;
- ;
- ; Loop through eraserSize+1 rows in bitMap for this point
- ;
- stampRowLoop:
- MOVE.W D1,D4 ; D4 = scribble h - origin
-
- TST.W gMode(A0)
- BEQ @L3
- BGT @L2
- LSR.W #1,D4 ; adjust for qrtr-scale bitmap
- L2: LSR.W #1,D4 ; adjust for half-scale bitmap
- L3:
- BFINS D3,(A1){D4:D0} ; insert mask D3 of width D0
- ; into bitmap A1 at offset D4
- ADDA.L D5,A1 ; increment by rowBytes
- DBRA D6,@stampRowLoop
- ;
- ; Fetch next deltaPoint, quit if done
- ;
- MOVE.L (A2)+,D0
- BEQ @bitMapDone
- ADD.W D0,D1 ; update current scribble h
- SWAP D0
- ADD.W D0,D2 ; update current scribble v
- BRA.S @stampPointLoop
-
- bitMapDone:
- ;
- ; Sort scribble points for fast lookup. Simple exchange
- ; sort will do.
- ;
- outerSortLoop:
- MOVEQ #0,D6 ; exchange flag = no exchanges
- LEA kSortedPoints(A0),A2; sorted point storage
- MOVE.W gNumPoints(A0),D7 ;outer loop counter
- SUBQ #2,D7 ; DBRA adjustment
- MOVE.L -(A2),D0 ; D0 = compare value - h
- MOVE.L D0,D1
- SWAP D1 ; D1 = compare value - v
- innerSortLoop:
- MOVE.L -(A2),D2
- MOVE.L D2,D3
- SWAP D3
- CMP.W D0,D2 ; primary sort by h
- BLT.S @doSwap
- BGT.S @noSwap
- CMP.W D1,D3 ; secondary sort by v
- BGE.S @noSwap
- doSwap:
- MOVE.L D2,4(A2)
- MOVE.L D0,(A2)
- MOVEQ #1,D6
- BRA.S @testInnerLoop
- noSwap:
- MOVE.L D2,D0
- MOVE.W D3,D1
- testInnerLoop:
- DBRA D7,@innerSortLoop
- TST.W D6
- BNE.S @outerSortLoop;
- MOVE.L #0x80008000,-(a2) ; Sentinal to end point loop
- MOVE.L #0x7FFF7FFF,-(a2) ; Sentinal to end point loop
-
- MOVE.L A0,D0 ; return pointer to private data
- MOVEM.L (A7)+,D3-D7/A2
- }
- }
-
-